# Copyright 1999-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

#
# Original Author: Serge V. Zarviroff <zarviroff@gmail.com>
# Purpose: make it easy to create and emerge ebuilds for packages' additives (such as mods, scripts, etc.;
# primarily designed for x3 - Reunion, but should work with any target app as it is very universal).
#
# Bugs, patches to zarviroff@gmail.com
#

# 
#   Usage requirements:
# 
#   An ebuild or eclass inheriting this eclass must provide two variables:
#       clns_TARGET_PREFIX containing full path to the clns_TARGET_NAME (w/o clns_TARGET_NAME)
#       clns_TARGET_NAME containing the name of a directory where the target package installed w/o the path
#
#   Optional:
#       A clns_STRICTORDER=1 could be provided for a package that shouldn't be uninstalled if it isn't the last installed one.
#       Otherwise, clns_STRICTORDER defaults to 0.
#       Set clns_SOURCESROOT to the actual root of the sources if clns_set_SOURCESROOT fails (never seen, though).
#       clns_TARGET_ROOT could be overrided. It defaults to "$clns_TARGET_PREFIX/$clns_TARGET_NAME" otherwise.
#
#       The real root of the source could be passed to clns_canonicalize_sources, clns_process_removelist
#       clns_handle_collisions and collisions_src_install functions as the first argument. They cd to clns_SOURCESROOT otherwise.
#       
#
#   Example:
#       for a package foo installed into /bar
#       clns_TARGET_PREFIX="/bar"
#       clns_TARGET_NAME="foo"
#
#
#   What it does:
#
# Makes a list of dirs/files that should be installed as the package most probably would load them (see clns_set_TARGETITEMS).
# Finds the real root of the sources (see clns_set_SOURCESROOT).
# Makes the sources correspond the clns_TARGETITEMS. Everything else goes to _clns_COLLISIONSROOT_P (see clns_canonicalize_sources).
# Synchronizes the source files with target' files (see clns_sync_names).
# Detects collisions (see clns_sync_names).
# Handles the collisions (see clns_handle_collisions, clns_analyze_collision, clns_process_lists, etc.).
#
# In the WORKDIR prior to the install phase:
#    For every package there is a _clns_COLLISIONSROOT created containing:
#        For every package there is a _clns_COLLISIONSROOT_P created containing:
#           a clns_file_FILELIST listing all files a package installs;
#           all other misc. files.
# In the clns_R_COLLISIONSROOT_P post the install phase:
#    A tracker_TRACKFILE containing the exact order of installations (updated as needed in ).
# In the clns_TARGET_ROOT there is a _clns_COLLISIONSROOT folder containing the backup folder.
#
# Install queue:
#    collisions_pkg_setup:       clns_set_ENV
#    collisions_post_src_unpack: clns_set_TARGETITEMS -> clns_set_SOURCESROOT
#    collisions_src_prepare:     clns_canonicalize_sources -> clns_handle_collisions
#                                  |-> clns_sync_names          |-> clns_analyze_collision -> clns_process_lists
#    collisions_pre_src_install: clns_process_backuplist
#    collisions_src_install:     make clns_file_FILELIST, do the install
#    collisions_pkg_postinst:    clns_STRICTORDER${clns_STRICTORDER}:${CATEGORY}/${PF} >> tracker_TRACKFILE
# Uninstall queue:
#   ollisions_pkg_prerm:         IfNotREINSTALL(don't allow to uninstall if this is a revdep or not the last clns_STRICTORDER'ed package)
#   collisions_pkg_postrm:       IfNotREINSTALL(clns_restore_files, clean clns_TARGET_ROOT)
#
 

inherit eutils tracker

EXPORT_FUNCTIONS pkg_setup post_src_unpack src_prepare pre_src_install src_install pkg_prerm pkg_postrm

#
# clns_set_ENV() funsction:
#
# This function called during the pkg_setup phase.
#
# Usage: clns_set_ENV target_prefix target_name (target_root)
clns_set_ENV() {
	# clns_TARGET_PREFIX and clns_TARGET_NAME have to be defined explicitly
	[[ -z "${1}" ]] && eerror "Please specify correct clns_TARGET_PREFIX." && die "clns_TARGET_PREFIX is not specified."
	[[ -z "${2}" ]] && eerror "Please specify correct clns_TARGET_NAME." && die "clns_TARGET_NAME is not specified."
	
	clns_TARGET_PREFIX="${1}"
	clns_TARGET_NAME="${2}"

	# If clns_TARGET_ROOT wasn't defined explicitly, then it is generated from clns_TARGET_PREFIX and clns_TARGET_NAME
	[[ -z "${3}" ]] && clns_TARGET_ROOT="${clns_TARGET_PREFIX}/${clns_TARGET_NAME}"

	if [[ ! -d "${clns_TARGET_ROOT}" ]]
	then
		eerror "${clns_TARGET_ROOT} doesn't exist or is not a directory."
		die "${clns_TARGET_ROOT} doesn't exist or is not a directory."
	fi

	_clns_COLLISIONSROOT="${clns_TARGET_NAME}-collisions"
	_clns_COLLISIONSROOT_P="${_clns_COLLISIONSROOT}/${CATEGORY}/${PN}"
	_clns_COLLISIONSROOT_P_MISC="${_clns_COLLISIONSROOT_P}/misc"
	_clns_COLLISIONSROOT_P_BACKUP="${_clns_COLLISIONSROOT_P}/backup"

	clns_R_COLLISIONSROOT="${clns_TARGET_ROOT}/${_clns_COLLISIONSROOT}"
	clns_R_COLLISIONSROOT_P="${clns_TARGET_ROOT}/${_clns_COLLISIONSROOT_P}"
	clns_R_COLLISIONSROOT_P_MISC="${clns_TARGET_ROOT}/${_clns_COLLISIONSROOT_P_MISC}"
	clns_R_COLLISIONSROOT_P_BACKUP="${clns_TARGET_ROOT}/${_clns_COLLISIONSROOT_P_BACKUP}"
	
	clns_ENV_IS_SET=1
}

#
# clns_set_TARGETITEMS() function:
#
# This function scans clns_TARGET_ROOT and creates an array clns_TARGETITEMS.
# clns_TARGETITEMS array is a list of clns_TARGET_ROOT' directories' names and files' extensions (i.e., .cat) stored in compliance with
# bash' regexp syntax.
#
clns_set_TARGETITEMS() {
	local IFS=$'\n'
	# some items like readmes or license infos shouldn't be listed in clns_TARGETITEMS
	local item
	for item in $(ls -A "${clns_TARGET_ROOT}" | sed '/readme/Id;/license/Id;/eula/Id;/changelog/Id;/'${_clns_COLLISIONSROOT}'/d')
	do
		# skip an item that was already added into the array
		[[ ${clns_TARGETITEMS} ]] && {
			local match=0;
			local i
			for (( i=0; "${i}" < "${#clns_TARGETITEMS[@]}"; ++i ))
			do
				[[ "${item}" =~ ^${clns_TARGETITEMS[${i}]}$ ]] && match=1;
			done
			(( "${match}" )) && continue;
		}

		[[ -d "${clns_TARGET_ROOT}/${item}" ]] && clns_TARGETITEMS[${#clns_TARGETITEMS[@]}]="${item}"

		# if item is a item then check if it has an extension (i.e., a dot symbol) and take appropriate actions
		[[ -f "${clns_TARGET_ROOT}/${item}" ]] && {
			echo "${item}" | grep -q "\." && clns_TARGETITEMS[${#clns_TARGETITEMS[@]}]=".*\.${item##*.}" || clns_TARGETITEMS[${#clns_TARGETITEMS[@]}]="${item}"
		}
	done
	[[ ! ${clns_TARGETITEMS} ]] && die "Failed to set clns_TARGETITEMS"
}

#
# clns_set_SOURCESROOT() function:
#
# This function sets clns_SOURCESROOT global variable.
# clns_SOURCESROOT != WORKDIR or S. clns_SOURCESROOT is a real root of a package a user installs.
# Most of mods and scripts distribution archives don't contain a root subfolder. So if such archive unpacked
# it's contents will reside in the WORKDIR. However, some do have a root subfolder containing all the stuff a
# distribution delivers. It's a mess, but in 99% cases the real root could be detected using this function.
#
# clns_set_SOURCESROOT() uses find' feature to print the depth of a query.
# Since a distribution might contain any number of subfolders or files of any depth, assume the real root of the source
# is the topmost direcoty containing any clns_TARGETITEMS' item. 
# It could appear slow, but it's really not.
#
clns_set_SOURCESROOT() {
	local IFS=$'\n'
	local mindepth=99
	local index
	for (( index=0; "${index}" < "${#clns_TARGETITEMS[@]}"; ++index ))
	do
		local depthpath=$(find . -iregex ".*/${clns_TARGETITEMS[${index}]}" -printf "%d:%h\n" | sort | head -n1)
		[[ ${depthpath%%:*} ]] && [[ "${depthpath%%:*}" != *[!0-9]* ]] && {
			if (( "${depthpath%%:*}" < "${mindepth}" ))
			then
				mindepth=${depthpath%%:*}
				clns_SOURCESROOT="${WORKDIR}/${depthpath#*:}"
			fi
		}
	done
	[[ ! ${clns_SOURCESROOT} ]] && die "Failed to find sources root. Looks like this package doesn't suit the target." || cd "${clns_SOURCESROOT}"
}

#
# clns_sync_names() function:
#
# This function gets called by clns_canonicalize_sources. It is very important. It synchronizes the sources with the target
# package files and detects any collisions that are handled later by clns_handle_collisions() function.
# 
# If the package is being reinstalled, then its filelist exist in the clns_R_COLLISIONSROOT_P. Items listed in there shouldn't be added
# into clns_COLLISIONS array and be handled by clns_handle_collisions() function. As they will be considered as clones and will be
# deleted prior to install phase, so installed files will be substituted with nothing.
#
clns_sync_names() {
	if [[ "${1}" == "" ]]
	then
		return
	fi

	local target="${1}"
	if [[ -f "${target}" ]]
	then
		# convert the ${name} to the _original_ -case (usually, lower)
		local canonicalname=$(echo "${target}" | sed 's/'${clns_TARGETITEMS[${index}]#.\*}'/'${clns_TARGETITEMS[${index}]#.\*}'/i')
		[[ "${target}" != "${canonicalname}" ]] && mv "${target}" "${canonicalname}"
		[[ -e "${clns_TARGET_ROOT}/${canonicalname}" ]] && clns_COLLISIONS[${#clns_COLLISIONS[@]}]="${canonicalname}"
	else
		local name
		for name in $(ls -A "${target}")
		do
			# this is a _sub_folder of one of the clns_TARGETITEMS in the WORKDIR, It is normal if it doesn't exist in the original package.
			local canonicalname="$(ls -A "${clns_TARGET_ROOT}/${target}" | grep -i "^${name}$")"
			if [[ -n "${canonicalname}" ]]
			then
				[[ "${target}/${name}$" != "${target}/${canonicalname}$" ]] && mv "${target}/${name}" "${target}/${canonicalname}"
				[[ -d "${target}/${canonicalname}" ]] && clns_sync_names "${target}/${canonicalname}" || clns_COLLISIONS[${#clns_COLLISIONS[@]}]="${target}/${canonicalname}"
			fi
		done
	fi
}

#
# clns_canonicalize_sources() function:
#
# This function makes the sources correspond the root structure of the target package.
#
# Everything in the clns_SOURCESROOT that doesn't look like any of clns_TARGETITEMS goes into _clns_COLLISIONSROOT_P.
# Otherwise, the match gets recursively synchronized with the clns_TARGET_ROOT by clns_sync_names().
#
clns_canonicalize_sources() {
	local IFS=$'\n'

	[[ -z "${1}" ]] && cd "${clns_SOURCESROOT}"

	[[ ! -d "${_clns_COLLISIONSROOT_P_MISC}" ]] && mkdir -p "${_clns_COLLISIONSROOT_P_MISC}"

	local name
	for name in $(ls -A | sed '/^'${_clns_COLLISIONSROOT}'/d')
	do
		local match=0
		local index
		for (( index=0; "${index}" < "${#clns_TARGETITEMS[@]}"; ++index ))
		do
			echo "${name}" | grep -qi ^${clns_TARGETITEMS[${index}]}$ && match=1 && break
		done
		if (( "${match}" ))
		then
			[[ -d "${name}" ]] && [[ "${name}" != "${clns_TARGETITEMS[${index}]}" ]] && mv "${name}" "${clns_TARGETITEMS[${index}]}" && name="${clns_TARGETITEMS[${index}]}"
			clns_sync_names "${name}"
		else
			mv "${name}" "${_clns_COLLISIONSROOT_P_MISC}"
		fi
	done
}

#
##############################################################
#
# These 4 small functions invoked by clns_analyze_collision(),
# Their duty is to make clns_analyze_collision() code clearer.
#
clns_add_to_removelist() {
	clns_removelist[${#clns_removelist[@]}]="${1}" # "${relpath}"
	clns_removelist[${#clns_removelist[@]}]="${2}" # "${relpathname}"
}

clns_add_to_backuplist() {
	clns_backuplist[${#clns_backuplist[@]}]="${1}" # "${relpath}"
	clns_backuplist[${#clns_backuplist[@]}]="${2}" # "${relpathname}"
}

clns_add_to_trickylist_resolvable() {
	clns_trickylist_rslv[${#clns_trickylist_rslv[@]}]="${1}" # "${relpathname}"
	clns_trickylist_rslv[${#clns_trickylist_rslv[@]}]="${2}" # "${owner_full}"
}

clns_add_to_trickylist_incompatible() {
	clns_trickylist_incm[${#clns_trickylist_incm[@]}]="${1}" # "${relpathname}"
	clns_trickylist_incm[${#clns_trickylist_incm[@]}]="${2}" # "${owner_full}"
}
##############################################################



#
##############################################################
#
# Lists pocessors are invoked by clns_process_lists() function.
# Each list processor is invoked only if certain list exists (has been
# prepared by clns_analyze_collision() function).
#

#
# clns_process_removelist() removes files that were determinated as duplicates of
# installed files by clns_analyze_collision().
#
clns_process_removelist() {
	local IFS=$'\n'

	[[ -z "${1}" ]] && cd "${clns_SOURCESROOT}"

	einfo "Removing duplicates from WORKDIR prior to install phase"
	local index
	for (( index=0; "${index}" < "${#clns_removelist[@]}"; index+=2 ))
	do
		ebegin "<<< ${clns_removelist[$((${index}+1))]}" # ${relpathname}
		rm -f "${clns_removelist[$((${index}+1))]}" || die "Failed to remove ${clns_removelist[$((${index}+1))]}"
		# remove the dir if it is empty
		[[ $(ls -A "${clns_removelist[${index}]}") ]] || rm -rf "${clns_removelist[${index}]}"
		eend
	done
}

#
# clns_process_backuplist() manipulates files in clns_TARGET_ROOT.
#
# It creates a folder clns_R_BACKUPDIR_P for backups of original files in clns_TARGET_ROOT.
# Then it moves files listed in clns_backuplist array from clns_TARGET_ROOT into clns_R_BACKUPDIR_P.
# That should be done this way to make a ebuild unmergable and original files restorable.
#
clns_process_backuplist() {
	addwrite "${clns_TARGET_ROOT}"
	local IFS=$'\n'

	einfo "Backing files up into [ ${clns_R_COLLISIONSROOT_P_BACKUP} ]:"
	local index
	for (( index=0; "${index}" < "${#clns_backuplist[@]}"; index+=2 ))
	do
		# ${clns_backuplist[${index}]} is relpath
		ebegin "<<< ${clns_backuplist[${index}]}"
			mkdir -p "${clns_R_COLLISIONSROOT_P_BACKUP}/${clns_backuplist[${index}]}" || die "Failed to create directory ${clns_R_COLLISIONSROOT_P_BACKUP}/${clns_backuplist[${index}]}"
		eend
		# ${clns_backuplist[$((${index}+1))]} is relpathname
		ebegin "<<< ${clns_backuplist[$((${index}+1))]}"
			mv "${clns_TARGET_ROOT}/${clns_backuplist[$((${index}+1))]}" "${clns_R_COLLISIONSROOT_P_BACKUP}/${clns_backuplist[$((${index}+1))]}" || die "Failed to move ${clns_TARGET_ROOT}/${clns_backuplist[$((${index}+1))]} to ${clns_R_COLLISIONSROOT_P_BACKUP}/${clns_backuplist[$((${index}+1))]}"
		eend
	done
}

#
# clns_process_trickylist_incompatible() lists information about files and it' owners
# whose presence makes this ebuild not installable.
# See clns_analyze_collision() logic for more information.
#
clns_process_trickylist_incompatible() {
	eerror ""
	eerror "This package tends to install a different version of a file(s) that"
	eerror "belongs to some another package(s) you installed before."
	eerror "Such action might make the target package behaviour unpredictable."
	eerror ""
	eerror "Here is the list:"
	eerror ""
	local index
	for (( index=0; "${index}" < "${#clns_trickylist_incm[@]}"; index+=2 ))
	do
		eerror "      file: ${clns_trickylist_incm[${index}]}" # relpathname
		eerror "     owner: =${clns_trickylist_incm[$((${index}+1))]}" # owner_full
	done
	eerror ""
	eerror "Unmerge every listed owner to install this package."
	eerror ""
}

#
# clns_process_trickylist_resolvable() lists files and it's owners information.
# It's pretty much self-documented.
# See clns_analyze_collision() for more.
#
clns_process_trickylist_resolvable() {
	eerror ""
	eerror "This package tends to install an original file(s) that"
	eerror "belongs to some another package you installed before."
	eerror ""
	eerror "This situation could appear resolvable."
	eerror ""
	eerror "Here is the list:"
	eerror ""
	local index
	for (( index=0; "${index}" < "${#clns_trickylist_rslv[@]}"; index+=2 ))
	do
		eerror "      file: ${clns_trickylist_rslv[${index}]}" # relpathname
		eerror "     owner: =${clns_trickylist_rslv[$((${index}+1))]}" # owner_full
		eerror "    fixcmd: mkdir -p ${FILESDIR} && echo '${clns_trickylist_rslv[${index}]}' >> ${FILESDIR}/forced-rm-list"
	done
	eerror ""
	eerror "Consult the developer for each file is really needed by the package."
	eerror ""
	eerror "If they were added by the developer accidently:"
	eerror "1. Execute listed fixcmd(s)."
	eerror "2. Move the ebuild into your local overlay and redigest it."
	eerror "3. Re-emerge this package."
	eerror ""
	eerror "Otherwise unmerge every listed owner to install this package."
	eerror ""
}
##############################################################

#
# clns_process_lists() function invokes lists processors for lists prepared by clns_analyze_collision() function.
#
# If there is one of the tricky lists were prepared then ebuild couldn't be merged niether without some manual actions
# or at all due to incompatibility with current install.
#
clns_process_lists() {
	if [[ ${clns_trickylist_incm} ]] || [[ ${clns_trickylist_rslv} ]]
	then
		eerror "Some collisions can't be handled automatically."
		[[ ${clns_trickylist_incm} ]] && clns_process_trickylist_incompatible
		[[ ${clns_trickylist_rslv} ]] && clns_process_trickylist_resolvable
		die "Package is incompatible with current install."
	fi
	[[ ${clns_removelist} ]] && clns_process_removelist
}

#
# clns_analyze_collision() function:
#
# Situations that could be faced during a package installation;
# a package installs a file that:
# 1.   is a clone of the installed original file;
# 2.   is a clone of the installed file that belongs to some other package installed previously;
# 3.   differs from the installed original file;
# 4.   differs from the installed file that belongs to some other package installed previously;
# 4.1    and clones the backuped original file;
# 4.2    and differs from the backuped original file.
# Solutions:
# 1.   delete the file from the clns_SOURCESROOT prior to the install phase;
# 2.   delete the file from the clns_SOURCESROOT prior to the install phase and create a REVDEP file blocking the unmergence of the owner;
# 3.   move the original file into clns_R_BACKUPDIR_P;
# 4.   abort the emergence prior to the install phase as the package is not compatible with current install. 
# 4.1    suggest contactacting the developer and an appropriate fix if it's safe to delete this file prior to the install phase,
#        or to unmerge a package that is blocking the install;
# 4.2    suggest to unmerge a package that is blocking the install.
# 
clns_analyze_collision() {
	local IFS=$'\n'

	local name="${1##*/}"
	local relpath="${1%/*}"
	local relpathname="${relpath}/${name}"

	[[ -e "${clns_R_COLLISIONSROOT}" ]] && local owner=$(tracker_findOwner "${clns_R_COLLISIONSROOT}" "${name}")
	if [[ -n "${owner}" ]]
	then
		if [[ "${owner}" == "${CATEGORY}/${PN}" ]]
		then
			# if that file is being reinstalled or updated (by the same PN) - it should be skipped
			return
		else
			local fullowner=$(tracker_getAtomIfInstalled "${clns_R_COLLISIONSROOT}" "${owner%/*}" "${owner#*/}")
			if diff -Bw "${relpathname}" "${clns_TARGET_ROOT}/${relpathname}" > /dev/null
			then
				[[ ! -e "${_clns_COLLISIONSROOT}/${owner}/${CATEGORY}-REVDEP-${PF}" ]] && ewarn "This package shall be dependent on ${owner}"
				[[ ! -d "${_clns_COLLISIONSROOT}/${owner}" ]] && mkdir -p "${_clns_COLLISIONSROOT}/${owner}"
				echo "${relpathname}" >> "${_clns_COLLISIONSROOT}/${owner}/${CATEGORY}-REVDEP-${PF}"
				clns_add_to_removelist "${relpath}" "${relpathname}"
			elif [[ -e "${clns_R_COLLISIONSROOT}/${owner}/backup/${relpathname}" ]]
			then
				if diff -Bw "${relpathname}" "${clns_R_COLLISIONSROOT}/${owner}/backup/${relpathname}" > /dev/null
				then
					clns_add_to_trickylist_resolvable "${relpathname}" "${fullowner}"
				else
					clns_add_to_trickylist_incompatible "${relpathname}" "${fullowner}"
				fi
			else
				clns_add_to_trickylist_incompatible "${relpathname}" "${fullowner}"
			fi
		fi
	elif diff -Bw "${relpathname}" "${clns_TARGET_ROOT}/${relpathname}" > /dev/null
	then
		clns_add_to_removelist "${relpath}" "${relpathname}"
	else
		clns_add_to_backuplist "${relpath}" "${relpathname}"
	fi
}

#
# clns_handle_collisions() function:
#
clns_handle_collisions() {
	local IFS=$'\n'

	[[ -z "${1}" ]] && cd "${clns_SOURCESROOT}"

	[[ -e "${FILESDIR}" ]] && {
		local line
		for line in $(cat "${FILESDIR}/forced-rm-list")
		do
			rm -f "${line}" || die "Failed to remove ${line}"
		done
	}

	local collision
	for (( collision=0; "${collision}" < "${#clns_COLLISIONS[@]}"; ++collision ))
	do
		clns_analyze_collision "${clns_COLLISIONS[${collision}]}"
	done

	clns_process_lists
}

collisions_pkg_setup() {
	# try to work. maybe the user defined clns_TARGET_NAME and clns_TARGET_PREFIX.
	[[ -z "${clns_ENV_IS_SET}" ]] && clns_set_ENV
}

collisions_post_src_unpack() {
	clns_set_TARGETITEMS
	clns_set_SOURCESROOT

	# forced-rm-list should be traversed prior to everything else to not interfere collisions lists
    [[ -e "${FILESDIR}" ]] && {
        local line
        for line in $(cat "${FILESDIR}/forced-rm-list")
        do
            rm -f "${clns_SOURCESROOT}/${line}" || die "Failed to remove ${line}"
        done
    }
}

collisions_src_prepare() {
	clns_canonicalize_sources
	clns_handle_collisions
	tracker_mktrackfile "${clns_R_COLLISIONSROOT}" "${clns_SOURCESROOT}/${_clns_COLLISIONSROOT}" "${CATEGORY}" "${PN}" "${PVR}" "${1}"
}

collisions_pre_src_install() {
	# clns_process_backuplist operates files in the clns_TARGET_ROOT; thus it is here
	[[ ${clns_backuplist} ]] && clns_process_backuplist
}

collisions_src_install() {
	[[ -z "${1}" ]] && cd "${clns_SOURCESROOT}"
	insinto ${clns_TARGET_ROOT}
	doins -r * || die "doins -r"
}

#
# pkg_prerm() function:
#
# Executed only if it is not a reinstall.
# Block an unmerge if:
# 1. there is a REVDEP-name file in the _P_ECLASSROOT, meaning that package _name_ depending on files listed in there;
# 2. it is not the last clns_STRICTORDER'ed package installed.
#
collisions_pkg_prerm() {
	if ! tracker_isReinstall
	then
		
		local revdep=0
		[[ $(ls -A "${clns_R_COLLISIONSROOT_P}" | grep ^.*-REVDEP-.*$) ]] && revdep=1

		local notlast=0
		(( "${tracker_STRICTORDER}" )) && ! $(tracker_isLastStrict "${clns_R_COLLISIONSROOT}" "${CATEGORY}" "${PN}") && notlast=1

		if (( "${revdep}" )) || (( "${notlast}" ))
		then
			eerror "This package can't be uninstalled right now."
			eerror ""
			if (( "${revdep}" ))
			then
				eerror "Unmerging this package will brake other package(s)."
				eerror ""
				local revdep_file
				for revdep_file in $(ls -A -1 ${clns_R_COLLISIONSROOT_P}/*-REVDEP-*)
				do
					revdep_filename="${revdep_file##*/}"
					eerror "=${revdep_filename%-REVDEP-*}/${revdep_filename#*-REVDEP-}"
					eerror "    depends on file(s):"
					local line
					for line in $(cat ${revdep_file})
					do
						eerror "        ${line}"
					done
					eerror ""
				done
			fi
			if (( "${notlast}" ))
			then
				eerror "This package can't be uninstalled as it is not the last strictordered"
				eerror "package installed and uninstalling it might lead to an unpredictable"
				eerror "target package behaviour."
				eerror ""
				tracker_printErrorsUntil "${clns_R_COLLISIONSROOT}" "${CATEGORY}" "${PN}" 1
				eerror ""
			fi
			eerror "Unmerge every listed package first, then this package could be unmerged."
			eerror ""
			eerror "Press Ctrl-C to Stop"
			eerror ""
			while : ; do sleep 60; done
		fi
	fi
}

clns_restore_files() {
	if [[ "${1}" == "" ]]
	then
		return
	fi
	local IFS=$'\n'

	local relpath="${1}"
	local name
	for name in $(ls -A "${1}")
	do
		
		local relpathname="${relpath}/${name}"
		[[ -d "${relpathname}" ]] && {
			clns_restore_files "${relpathname}"
			continue
		}
		ebegin ">>> ${relpathname#${clns_R_COLLISIONSROOT_P_BACKUP}/}"
			[[ ! -d "${clns_TARGET_ROOT}/${relpath#${clns_R_COLLISIONSROOT_P_BACKUP}}" ]] && mkdir -p "${clns_TARGET_ROOT}/${relpath#${clns_R_COLLISIONSROOT_P_BACKUP}}"
			mv "${relpathname}" "${relpathname/${clns_R_COLLISIONSROOT_P_BACKUP}/${clns_TARGET_ROOT}}" || die "Failed to restore ${relpathname}"
		eend
	done
}

#
# pkg_postrm() manipulates files in R_GAMEROOT
# It restores the original files from the backups.
#
# Executed only if it is not a reinstall.
#
collisions_pkg_postrm() {
	if ! tracker_isReinstall
	then
		if [[ -e "${clns_R_COLLISIONSROOT_P_BACKUP}" ]]
		then
			einfo "Restoring files from [ ${clns_R_COLLISIONSROOT_P_BACKUP} ]:"
			clns_restore_files "${clns_R_COLLISIONSROOT_P_BACKUP}" || die "Failed to restore original files."
			rm -rf "${clns_R_COLLISIONSROOT_P_BACKUP}" || die "Failed to remove ${clns_R_COLLISIONSROOT_P_BACKUP}"
			echo "<<<          dir ${clns_R_COLLISIONSROOT_P_BACKUP}"
		fi
		
		if [[ -e "${clns_R_COLLISIONSROOT_P}" ]]
		then
			if (( "$(ls -A ${clns_R_COLLISIONSROOT_P} | wc -l)" ))
			then
				echo "--- !empty   dir ${clns_R_COLLISIONSROOT_P}"
			else
				rm -fr "${clns_R_COLLISIONSROOT_P}" || die "Failed to remove ${clns_R_COLLISIONSROOT_P}"
				echo "<<<          dir ${clns_R_COLLISIONSROOT_P}"
			fi
		fi
		
		if [[ -e "${clns_R_COLLISIONSROOT}/${CATEGORY}" ]]
		then
			if (( "$(ls -A ${clns_R_COLLISIONSROOT}/${CATEGORY} | wc -l)" ))
			then
				echo "--- !empty   dir ${clns_R_COLLISIONSROOT}/${CATEGORY}"
			else
				rm -fr "${clns_R_COLLISIONSROOT}/${CATEGORY}" || die "Failed to remove ${clns_R_COLLISIONSROOT}/${CATEGORY}"
				echo "<<<          dir ${clns_R_COLLISIONSROOT}/${CATEGORY}"
			fi
		fi
		
		if [[ -e "${clns_R_COLLISIONSROOT}" ]]
		then
			if [[ $(ls -A "${clns_R_COLLISIONSROOT}") ]]
			then
				echo "--- !empty   dir ${clns_R_COLLISIONSROOT}"
			else
				rm -fr "${clns_R_COLLISIONSROOT}" || die "Failed to remove ${clns_R_COLLISIONSROOT}"
				echo "<<<          dir ${clns_R_COLLISIONSROOT}"
			fi
		fi
	fi
	
	tracker_clean
}
